/*
 * @file arma_base.h
 *
 * Inclusion of Armadillo
 *
 * @author Dahua Lin
 */

#ifndef _MSC_VER
#pragma once
#endif

#ifndef GMLPP_ARMA_BASE_H_
#define GMLPP_ARMA_BASE_H_

#include <gmlpp/base/basic_defs.h>
#include <gmlpp/base/basic_math.h>
#include <gmlpp/base/arg_check.h>
#include <gmlpp/base/mem_op.h>

#ifndef ARMA_INCLUDED
#include <armadillo>
#define ARMA_INCLUDED
#endif

namespace gml
{
	using arma::uword;
	using arma::sword;

	using arma::Mat;
	using arma::Col;
	using arma::Row;

	using arma::mat;
	using arma::colvec;
	using arma::rowvec;

	/******************************************************
	 *
	 *  Handy functions
	 *
	 ******************************************************/

	template<typename T>
	GML_ENSURE_INLINE inline bool has_size(const Mat<T>& X, uword m, uword n)
	{
		return X.n_rows == m && X.n_cols == n;
	}

	template<typename T1, typename T2>
	GML_ENSURE_INLINE inline bool is_same_size(const Mat<T1>& X, const Mat<T2>& Y)
	{
		return X.n_rows == Y.n_rows && X.n_cols == Y.n_cols;
	}

	template<typename T>
	inline bool is_equal(const Mat<T>& X, const Mat<T>& Y)
	{
		if (is_same_size(X, Y))
		{
			 return mem<T>::equal(X.n_elem, X.memptr(), Y.memptr());
		}
		else
		{
			return false;
		}
	}

	template<typename T>
	inline bool is_approx(const Mat<T>& X, const Mat<T>& Y, T tol)
	{
		if (is_same_size(X, Y))
		{
			uword n = X.n_elem;

			const T *x = X.memptr();
			const T *y = Y.memptr();

			for (uword i = 0; i < n; ++i)
			{
				if (std::abs(x[i] - y[i]) > tol) return false;
			}
			return true;
		}
		else
		{
			return false;
		}
	}


	template<typename T>
	GML_ENSURE_INLINE inline void symmetrify(Mat<T>& X)
	{
		X = 0.5 * (X + X.t());
	}

	template<typename T>
	inline void clamp_top(Mat<T>& X, const T& ub)
	{
		uword n = X.n_elem;

		T *x = X.memptr();
		for (uword i = 0; i < n; ++i)
		{
			if (x[i] > ub) x[i] = ub;
		}
	}

	template<typename T>
	inline void clamp_bottom(Mat<T>& X, const T& lb)
	{
		uword n = X.n_elem;

		T *x = X.memptr();
		for (uword i = 0; i < n; ++i)
		{
			if (x[i] < lb) x[i] = lb;
		}
	}

	template<typename T>
	inline void clamp(Mat<T>& X, const T& lb, const T& ub)
	{
		uword n = X.n_elem;

		T *x = X.memptr();
		for (uword i = 0; i < n; ++i)
		{
			if (x[i] < lb) x[i] = lb;
			else if (x[i] > ub) x[i] = ub;
		}
	}
}


#endif /* ARMA_BASE_H_ */
